﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;

using Microsoft.WindowsAPICodePack.Shell;

namespace My_TV_Manager.Model
{
    public class Episode : ISerializable, IComparable
    {
        #region Private Data

        // File name: <Formatted Title>.<Extension>
        private string fileName;
        private string ext;

        // Formatted title: <Series Name> - S<Season Number>E<Episode Number> - <Episode Title>
        private string series;
        private int season;
        private int episode;
        private string title;
        private string formatted;

        // Media File Information
        private int minutes;
        private int seconds;
        private int width;
        private int height;
        private double frameRate;
        private int totalBitrate;
        private int dataRate;
        private int audioBitrate;
        private int audioSampleRate;

        // Date-Time information
        private DateTime lastModified;
        private DateTime lastAccessed;

        // TVDB Additional Data
        private int episodeId;
        private DateTime firstAired;

        #endregion

        #region Properties

        // Properties to access information from outside class
        public string FileName { get { return fileName; } }
        public string Extension { get { return ext; } }
        public string SeriesName { get { return series; } }
        public int SeasonNumber { get { return season; } }
        public int EpisodeNumber { get { return episode; } }
        public string Title { get { return title; } }
        public string FormattedTitle { get { return formatted; } }
        public double LengthTotal { get { return minutes + seconds / 60; } }
        public int LengthMinutes { get { return minutes; } }
        public int LengthSeconds { get { return seconds; } }
        public int VideoWidth { get { return width; } }
        public int VideoHeight { get { return height; } }
        public double AspectRatio { get { return (double)width / height; } }
        public double FrameRate { get { return frameRate; } }
        public int TotalBitRate { get { return totalBitrate; } }
        public int DataRate { get { return dataRate; } }
        public int AudioBitRate { get { return audioBitrate; } }
        public int AudioSampleRate { get { return audioSampleRate; } }
        public DateTime LastModified { get { return lastModified; } }
        public DateTime LastAccessed { get { return lastAccessed; } }
        public int EpisodeID { get { return episodeId; } }
        public DateTime FirstAired { get { return firstAired; } }

        #endregion

        #region Constructors

        // Default constructor for testing
        public Episode()
        {
            series = "";
            season = 0;
            episode = 0;
            title = "";
            formatted = "";

            minutes = 0;
            seconds = 0;
            width = 0;
            height = 0;
            frameRate = 0.0;
            totalBitrate = 0;
            dataRate = 0;
            audioBitrate = 0;
            audioSampleRate = 0;

            lastModified = DateTime.Now;
            lastAccessed = DateTime.Now;
        }

        // Constructor to create a new episode
        public Episode(string series, int seasonNumber, int episodeNumber, string title)
        {
            this.series = series;
            season = seasonNumber;
            episode = episodeNumber;
            this.title = title;

            formatted = series + " - S";
            if (seasonNumber < 10) { formatted += "0"; }
            formatted += seasonNumber.ToString() + "E";
            if (episodeNumber < 10) { formatted += "0"; }
            formatted += episodeNumber.ToString() + " - " + title;

            lastModified = DateTime.Now;
            lastAccessed = DateTime.Now;
        }

        // Constructor to create a new series at a specific time
        public Episode(string series, int seasonNumber, int episodeNumber, string title, DateTime lastModified)
        {
            this.series = series;
            season = seasonNumber;
            episode = episodeNumber;
            this.title = title;

            formatted = series + " - S";
            if (seasonNumber < 10) { formatted += "0"; }
            formatted += seasonNumber.ToString() + "E";
            if (episodeNumber < 10) { formatted += "0"; }
            formatted += episodeNumber.ToString() + " - " + title;

            this.lastModified = lastModified;
            lastAccessed = DateTime.Now;
        }

        // Constructor which uses a file path to compile information
        public Episode(string path)
        {
            int loc = path.LastIndexOf('\\') + 1;
            fileName = path.Substring(loc);
            ext = fileName.Substring(fileName.Length - 3);

            loc = fileName.IndexOf('-') - 1;
            series = fileName.Substring(0, loc);
            season = int.Parse(fileName.Substring(loc + 4, 2));
            episode = int.Parse(fileName.Substring(loc + 7, 2));

            loc = fileName.IndexOf('-', loc + 4);
            title = fileName.Substring(loc + 2, fileName.Length - loc - 6);
            formatted = fileName.Substring(0, fileName.Length - 4);

            ShellFile sFile = ShellFile.FromFilePath(path);

            double nano;
            double.TryParse(sFile.Properties.System.Media.Duration.Value.ToString(), out nano);
            double min = nano / 600000000;
            minutes = (int)Math.Floor(min);
            seconds = (int)Math.Floor((min % 1.0) * 60);

            int.TryParse(sFile.Properties.System.Video.FrameWidth.Value.ToString(), out width);
            int.TryParse(sFile.Properties.System.Video.FrameHeight.Value.ToString(), out height);

            double.TryParse(sFile.Properties.System.Video.FrameRate.Value.ToString(), out frameRate);
            frameRate /= 1000;

            int.TryParse(sFile.Properties.System.Video.TotalBitrate.Value.ToString(), out totalBitrate);
            int.TryParse(sFile.Properties.System.Video.EncodingBitrate.Value.ToString(), out dataRate);
            int.TryParse(sFile.Properties.System.Audio.EncodingBitrate.Value.ToString(), out audioBitrate);
            int.TryParse(sFile.Properties.System.Audio.SampleRate.Value.ToString(), out audioSampleRate);

            lastModified = (DateTime)sFile.Properties.System.DateModified.Value;
            lastAccessed = (DateTime)sFile.Properties.System.DateAccessed.Value;

            episodeId = 0;
        }

        // Constructor which uses an XML node to compile information
        public Episode(string seriesName, XmlNode node)
        {
            series = seriesName;

            int.TryParse(node["SeasonNumber"].InnerText, out season);
            int.TryParse(node["EpisodeNumber"].InnerText, out episode);
            title = node["EpisodeName"].InnerText;

            formatted = series + " - S";
            if (season < 10) { formatted += "0"; }
            formatted += season.ToString() + "E";
            if (episode < 10) { formatted += "0"; }
            formatted += episode.ToString() + " - " + title;

            int.TryParse(node["id"].InnerText, out episodeId);

            string date = node["FirstAired"].InnerText;
            if (date != "")
            {
                firstAired = new DateTime(int.Parse(date.Substring(0, 4)),
                                          int.Parse(date.Substring(5, 2)),
                                          int.Parse(date.Substring(8, 2)));
            }
        }

        #endregion

        #region Functions

        // Recursive print
        public void Print()
        {
            Console.Out.WriteLine("\t\t" + formatted);
        }

        #endregion

        #region ISerializable Members

        // Deserialization constructor
        public Episode(SerializationInfo info, StreamingContext context)
        {
            fileName = info.GetString("FileName");
            ext = info.GetString("Extension");
            series = info.GetString("SeriesName");
            season = info.GetInt32("SeasonNumber");
            episode = info.GetInt32("EpisodeNumber");
            title = info.GetString("Title");
            formatted = info.GetString("FormattedTitle");
            minutes = info.GetInt32("LengthMinutes");
            seconds = info.GetInt32("LengthSeconds");
            width = info.GetInt32("VideoWidth");
            height = info.GetInt32("VideoHeight");
            frameRate = info.GetInt32("FrameRate");
            totalBitrate = info.GetInt32("TotalBitRate");
            dataRate = info.GetInt32("DataRate");
            audioBitrate = info.GetInt32("AudioBitRate");
            audioSampleRate = info.GetInt32("AudioSampleRate");
            lastModified = info.GetDateTime("LastModified");
            lastAccessed = info.GetDateTime("LastAccessed");
            episodeId = info.GetInt32("EpisodeID");
            firstAired = info.GetDateTime("FirstAired");
        }

        // Implemented for serialization
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("FileName", fileName);
            info.AddValue("Extension", ext);
            info.AddValue("SeriesName", series);
            info.AddValue("SeasonNumber", season);
            info.AddValue("EpisodeNumber", episode);
            info.AddValue("Title", title);
            info.AddValue("FormattedTitle", formatted);
            info.AddValue("LengthMinutes", minutes);
            info.AddValue("LengthSeconds", seconds);
            info.AddValue("VideoWidth", width);
            info.AddValue("VideoHeight", height);
            info.AddValue("FrameRate", frameRate);
            info.AddValue("TotalBitRate", totalBitrate);
            info.AddValue("DataRate", dataRate);
            info.AddValue("AudioBitRate", audioBitrate);
            info.AddValue("AudioSampleRate", audioSampleRate);
            info.AddValue("LastModified", lastModified);
            info.AddValue("LastAccessed", lastAccessed);
            info.AddValue("EpisodeID", episodeId);
            info.AddValue("FirstAired", firstAired);
        }

        #endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            if (obj is Episode)
            {
                Episode s = (Episode)obj;
                return episode.CompareTo(s.EpisodeNumber);
            }
            else
            {
                throw new ArgumentException("Object is not of type Episode");
            }
        }

        #endregion
    }
}
